package queue

import (
	"context"
	"encoding/hex"
	"encoding/json"
	"math/rand"
	"strconv"
	"time"
)

// IQueue 消息队列接口，抽象不同实现的核心操作
type (
	IQueue interface {
		Topic() string                                            //获取队列主题
		Driver() string                                           //获取驱动类型
		Retry() int                                               //消息重试次数.
		Group() string                                            //消费组名
		Dlq() bool                                                //是否启用死信队列，如果启用超过重试次数后会放入
		Ttl() time.Duration                                       //消息存活时间
		Produce(ctx context.Context, msg []byte, delay int) error //生产消息，delay大于0则是延迟消息
		Consume(ctx context.Context, qm QueueMessgae) bool        //消费消息
		Options() Options                                         //获取配置
	}
)

// 队列基类，其他队列需要继承它，否则需要自己实现队列方法
type Queue struct {
}

func (q *Queue) Topic() string {
	panic("必须实现获取主题的方法！")
}

func (q *Queue) Driver() string {
	panic("必须实现获取驱动类型的方法！")
}

func (q Queue) Consume(ctx context.Context, msg QueueMessgae) bool {
	panic("必须实现消费消息的方法！")
}

func (q *Queue) Produce(ctx context.Context, msg []byte, delay int) (err error) {
	driver := GetDriver(q.Driver())
	qm := &QueueMessgae{
		Payload: msg,
		Headers: map[string]interface{}{
			HeaderTopic:     q.Topic(),
			HeaderDelivery:  "delivery",
			HeaderExpiredAt: time.Now().Add(q.Ttl()).Unix(),
		},
	}
	message, err := json.Marshal(qm)
	if err != nil {
		return
	}
	return driver.Publish(ctx, q.Topic(), message, delay)
}

func (q *Queue) Retry() int {
	return 0
}

func (q *Queue) Dlq() bool {
	return false
}

func (q *Queue) Group() string {
	return "default"
}

func (q *Queue) Ttl() time.Duration {
	return time.Second * 86400
}

func (q *Queue) Options() Options {
	return NewOptions(WithConsumerGroup(q.Group()), WithRetry(q.Retry()), WithBatchNumber(10), WithTTL(86400))
}

func (q *Queue) UniqueId() string {
	b := make([]byte, 8)
	if _, err := rand.Read(b); err != nil {
		return strconv.FormatInt(time.Now().UnixNano(), 10)
	}
	return hex.EncodeToString(b)
}
